package rtool

import (
	"fmt"
	"log"
	"path"
	"reflect"
	"runtime"
	"time"
)

var (
	RetryNotAFunction      = fmt.Errorf("not a function")
	RetryParamsLengthError = fmt.Errorf("parameter length error")
	RetryParamsTypeError   = fmt.Errorf("parameter type error")
)

// RetryFunc 如果函数运行失败则重试，如果函数运行成功则结束
// maxBout：最大重试次数
// sleepTime：重试间隔时间
// ptLog：是否打印错误日志
// fx：要执行的函数，函数返回值1：是否继续重试至最大次数；函数返回值2：错误信息
// 返回值：错误信息
func RetryFunc(maxBout int, sleepTime time.Duration, ptLog bool, fx func() (bool, error)) (err error) {
	if maxBout <= 0 {
		maxBout = 1
	}
	for i := 0; i < maxBout; i++ {
		ct, er := fx()
		err = er
		if false == ct {
			return er
		}
		if er == nil {
			return nil
		}
		if ptLog {
			pc, file, line, _ := runtime.Caller(1)
			prev := fmt.Sprint(path.Base(file), ":", line, ":")
			log.Println(prev, getFuncName(pc), "函数运行错误：", i+1, "/", maxBout, "：", er)
		}
		if sleepTime > 0 {
			time.Sleep(sleepTime)
		}
	}
	return
}

// RetryFuncErr 如果函数运行失败则重试，如果函数运行成功则结束
// maxBout：最大重试次数
// sleepTime：重试间隔时间
// ptLog：是否打印错误日志
// fx：要执行的函数，函数返回值1：是否继续重试至最大次数；函数返回值2：错误信息
// 返回值：错误信息
func RetryFuncErr(maxBout int, sleepTime time.Duration, ptLog bool, fx func() error) (err error) {
	if maxBout <= 0 {
		maxBout = 1
	}
	for i := 0; i < maxBout; i++ {
		err = fx()
		if err == nil {
			return
		}
		if ptLog {
			pc, file, line, _ := runtime.Caller(1)
			prev := fmt.Sprint(path.Base(file), ":", line, ":")
			log.Println(prev, getFuncName(pc), "函数运行错误：", i+1, "/", maxBout, "：", err)
		}
		if sleepTime > 0 {
			time.Sleep(sleepTime)
		}
	}
	return
}

// Retry 如果函数运行失败则重试，如果函数运行成功则结束
// maxBout：最大重试次数
// sleepTime：重试间隔时间
// fx：要执行的函数；func(...)error 出错重试，或func(...)(bool,error) 返回true并且出错才重试。
// params：参数列表，参数列表长度与类型必须和fx一致。
// 返回值：错误信息
func Retry(maxBout int, sleepTime time.Duration, fx any, params ...any) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("%v", r)
		}
	}()
	runFunc := reflect.ValueOf(fx)
	for runFunc.Kind() == reflect.Ptr {
		runFunc = runFunc.Elem()
	}

	if runFunc.Kind() != reflect.Func {
		return RetryNotAFunction
	}

	//获取函数的参数列表长度
	pLength := runFunc.Type().NumIn()
	if len(params) != pLength {
		return RetryParamsLengthError
	}

	//遍历参数列表，对比参数类型是否一致
	for i := 0; i < pLength; i++ {
		t1 := reflect.TypeOf(params[i])
		t2 := reflect.New(runFunc.Type().In(i)).Elem()
		t1Kind := t1.Kind()
		t2Kind := t2.Kind()
		if t1Kind == reflect.Interface || t1Kind == reflect.Pointer {
			t1Kind = t1.Elem().Kind()
		}
		if t2Kind == reflect.Interface || t2Kind == reflect.Pointer {
			t2Kind = reflect.Indirect(reflect.ValueOf(runFunc.Type().In(i))).Kind()
		}
		if t1Kind != t2Kind {
			if t2.Kind() == reflect.Pointer {
				if t1.String() == t2.Type().String() {
					continue
				}
			}
			return RetryParamsTypeError
		}
	}

	//重试机制
	for i := 0; i <= maxBout; i++ {
		in := make([]reflect.Value, len(params))
		for k, param := range params {
			in[k] = reflect.ValueOf(param)
		}
		results := runFunc.Call(in)
		if len(results) >= 0 {
			isContinue := true //出错是否需要继续运行

			errAny := results[0].Interface()
			if len(results) == 2 {
				//返回值有两个的情况下，第一个为bool类型，出错是否继续运行。第二个为error类型。
				if errAny != nil {
					if _bl, ok := errAny.(bool); ok {
						isContinue = _bl
					}
				}
				errAny = results[1].Interface()
			}

			//判断是否出错
			if _err, ok := errAny.(error); ok {
				if _err != nil {
					if i == maxBout {
						return _err
					}
					if !isContinue { //不继续运行的，立即返回
						return _err
					}
					if sleepTime > 0 {
						time.Sleep(sleepTime)
					}
					continue
				}
			}
		}
		return nil
	}
	return nil
}
